/**
 * Mini-DSL for manipulating measures and units by leveraging the JScience library
 * Reference: Laforge, Guillaume. A Domain-Specific Languages for unit manipulations.
 * http://groovy.dzone.com/news/domain-specific-language-unit-, March 2008.
 *
 * User: mstine
 * Date: Mar 18, 2008
 * Time: 11:47:49 PM
 */
import javax.measure.unit.*
import org.jscience.physics.amount.*

// Allow ExpandoMetaClass to traverse class hirarchies
// That way, properties added to Number will aleso be available for Integer or BigDecimal, etc.
ExpandoMetaClass.enableGlobally()
// Set JScience to display a format for which only digits guaranteed to be exact are written out.
//AmountFormat.setInstance(AmountFormat.getExactDigitsInstance())

// transform number properties into an mount of a given unit represented by the property
Number.metaClass.getProperty = { String symbol -> Amount.valueOf(delegate, Unit.valueOf(symbol)) }

// sample units
print "2 kg = "
println( 2.kg )

print "3 m = "
println( 3.m )

print "4.5 in = "
println( 4.5.in )

// define opeartor overloading, as JScience doesn't use the same operation names as Groovy
Amount.metaClass.multiply = { Number factor -> delegate.times(factor) }
Number.metaClass.multiply = { Amount amount -> amount.times(delegate) }
Number.metaClass.div = { Amount amount -> amount.inverse().times(delegate) }
Amount.metaClass.div = { Number factor -> delegate.divide(factor) }
Amount.metaClass.div = { Amount factor -> delegate.divide(factor) }
Amount.metaClass.power = { Number factor -> delegate.pow(factor) }
Amount.metaClass.negative = { -> delegate.opposite() }

// arithmetics: multiply, divide, addition, substraction, power
print "18.4 kg * 2 = "
println( 18.4.kg * 2 )

print "1800000 kg / 3 = "
println( 1800000.kg / 3 )

print "1 kg * 2 + 3 kg / 4 = "
println( 1.kg * 2 + 3.kg / 4 )

print "3 cm + 12 m * 3 - 1 km = "
println( 3.cm + 12.m * 3 - 1.km )

print "1.5 h + 33 s - 12 min = "
println( 1.5.h + 33.s - 12.min )

print "30 m ** 2 - 100 ft ** 2 = "
println( 30.m**2 - 100.ft**2 )

// opposite and comparison
println( -3.h )
println( 3.h < 4.h )

// script binding to transform free standing unit reference like 'm', 'h', etc
class UnitBinding extends Binding {
    def getVariable(String symbol) {
        if (symbol == 'out') return System.out
        return Amount.valueOf(1, Unit.valueOf(symbol))
    }
}

// use the script binding for retrieving unit references
binding = new UnitBinding()

// inverse units
println( 30.km/h + 2.m/s * 2 )
println( 3 * 3.mg/L )
println( 1/2.s - 2.Hz )

// define to() method for unit conversion
Amount.metaClass.to = { Amount amount -> delegate.to(amount.unit) }

// unit conversion
println( 200.cm.to(ft) )
println( 1.in.to(cm) )
